package br.com.felix.fwt.ui;

import java.io.PrintWriter;
import java.util.HashMap;
import java.util.Map;

import org.apache.log4j.Logger;

import br.com.felix.fwt.datatype.Coordinate;
import br.com.felix.fwt.log.LoggerFactory;
import br.com.felix.fwt.ui.css.Align;
import br.com.felix.fwt.ui.css.Size;
import br.com.felix.fwt.ui.BaseComponent;
import br.com.felix.fwt.ui.exception.ComponentRenderException;

/**
 * The Class Table implements the HTML table.
 * It ignores the childs defined in the BaseComponent.
 * Components must be added throught the appropriate methods.
 */
public class Table extends BaseComponent{

	private static final long serialVersionUID = 1024217217410073880L;

	/** The max y. */
	private int maxX = 0, maxY = 0;
	
	/** The cells. */
	private Map<Coordinate,Cell>cells;
	
	/** The width. */
	public Size width;
	
	/** The align. */
	public Align align;
	
	/**
	 * Instantiates a new table.
	 */
	public Table(){
		this.cssClass = "fwttable";
		cells = new HashMap<Coordinate,Cell>();
	}
	
	private static Logger logger = LoggerFactory.getLogger(Table.class);
	
	/**
	 * Sets a component in a table position.
	 * 
	 * @param component the component to be set
	 * @param column the column
	 * @param row the row
	 */
	public void setComponent(BaseComponent component, int column, int row){
		super.add(component);
		Coordinate coord = new Coordinate(column,row);
		Cell cell = cells.get(coord);
		if (cell == null){
		    cell = new Cell();
		}
		cell.component = component;
		
        cells.put(coord, cell);
		maxX = Math.max(maxX, column);
		maxY = Math.max(maxY, row);
	}
	
	
	public void removeComponent(int column, int row){
		Coordinate coordinate = new Coordinate(column,row);
		Cell cell = cells.get(coordinate);
		if(cell!=null){
			super.remove(cell.component);
		}
		cells.remove(coordinate);
		recalculateMaxes();
	}
	
	
	private void recalculateMaxes() {
		int maxX=0;
		int maxY=0;
		for(Coordinate coord:cells.keySet()){
			maxX = Math.max(maxX, coord.x);
			maxY = Math.max(maxY, coord.y);
		}
		this.maxX = maxX;
		this.maxY = maxY;
	}


	/**
	 * Returns a reference to the component at the specified coordinates.
	 * @return a reference to the component at the specified coordinates. Null if there is no component at the specified coordinates.
	 * */
	public BaseComponent getComponent(int column, int row){
		return cells.get(new Coordinate(column,row)).component;
	}
	
	/**
	 * Sets a text in a table position.
	 * 
	 * @param string the text to be set
	 * @param column the column
	 * @param row the row
	 */
	public void setText(String string, int column, int row) {
	    setComponent(new Html(string), column, row);
	}
	
	
	/**
     * Sets a text in a table position.
     * Calls toString on the informed object.
     * 
     * @param object the text to be set
     * @param column the column
     * @param row the row
     */
    public void setText(Object object, int column, int row) {
        setText(object.toString(),column, row);
    }
	
	
	
	/**
	 * Removes a component from the table.
	 * 
	 * @param component the component
	 */
	public synchronized void removeComponent(BaseComponent component){
		childs.remove(component);
		maxX = 0;
		maxY = 0;
		Coordinate keyComp = null;
		for(Coordinate key: cells.keySet()){
			BaseComponent comp = cells.get(key).component;
			if(comp == component){
				keyComp = key;
			}
			else{
				maxX = Math.max(maxX, key.x);
				maxY = Math.max(maxY, key.y);
			}
		}
		if(keyComp != null){
			cells.remove(keyComp);
		}
	}
	
	
	/**
	 * Removes the entire line of a component from the table.
	 * 
	 * @param component a component that is at the line that will be removed
	 */
	public synchronized void removeEntireLineOfComponent(BaseComponent component){
		Coordinate keyComp = getComponentCoordinate(component);
		
		if(keyComp != null){
			logger.debug("Found keycomp at ("+keyComp.x+","+keyComp.y+")");
			Coordinate coord = new Coordinate();
			coord.y = keyComp.y;
			for(int x=0; x <= maxX; x++){
				coord.x = x;
				Cell cell = cells.get(coord);
				if(cell!=null){
					logger.debug("Removing component at ("+coord.x+","+coord.y+")");
					childs.remove(cell.component);
					cells.remove(coord);	
				}
			}
			for(int y=keyComp.y+1; y<=maxY;y++){
				for(int x=0; x <= maxX; x++){
					coord.x=x;
					coord.y=y;
					Cell cell = cells.get(coord);
					if(cell!=null){
						logger.debug("Moving component from("+coord.x+","+coord.y+")");
						cells.remove(coord);
						logger.debug("Moving component to("+coord.x+","+(coord.y-1)+")");
						cells.put(new Coordinate(coord.x,coord.y-1),cell);
					}
				}
			}
		}
		recalculateIndexes();
	}
	
	
	/**
     * Removes the entire line of a component from the table.
     * 
     * @param component a component that is at the line that will be removed
     */
    public synchronized void removeRow(int row){
        Coordinate coord = new Coordinate();
        coord.y = row;
        for(int x=0; x <= maxX; x++){
            coord.x = x;
            Cell cell = cells.get(coord);
            if(cell!=null){
                logger.debug("Removing component at ("+coord.x+","+coord.y+")");
                childs.remove(cell.component);
                cells.remove(coord);    
            }
        }
        Coordinate newCoordinate;
        for(int y=row+1; y <= maxY; y++){
            for(int x=0; x <= maxX; x++){
                coord.y = y;
                coord.x = x;
                Cell cell = cells.get(coord);
                if(cell!=null){
                    logger.debug("Replacing component at ("+coord.x+","+coord.y+")");
                    //childs.remove(comp);
                    cells.remove(coord);
                    newCoordinate = new Coordinate(x,y-1);
                    cells.put(newCoordinate, cell);
                }
            }
        }
        recalculateIndexes();
    }
	

	/**
	 * Recalculate the internal state variables maxX and maxY.
	 * */
	private void recalculateIndexes() {
		int maxX = 0;
		int maxY = 0;
		for(Coordinate coord: cells.keySet()){
			maxX = Math.max(maxX, coord.x);
			maxY = Math.max(maxY, coord.y);
		}
		this.maxX = maxX;
		this.maxY = maxY;
		logger.debug("MaxX = "+this.maxX+", MaxY = "+this.maxY);
	}

	
	/* (non-Javadoc)
	 * @see br.com.felix.fwt.ui.BaseComponent#write(java.io.PrintWriter)
	 */
	@Override
	public void write(PrintWriter out) throws ComponentRenderException {
		if(!authorized()) return;
		out.write("\n<table class=\"");
		out.write(cssClass);
		out.write("\" ");
		if(this.style!=null){
			out.write(style);
		}
		if(this.width!=null){
			out.write(" width=\"");
			out.write(width.value());
			out.write("\"");
		}
		if(this.align!=null){
			out.write("align=\"");
			out.write(align.toString());
			out.write("\" ");
		}
		out.write(">");
		Cell cell;
		Integer rowspan, colspan;
		VerticalAlign valign;
		String cssClass;
		Coordinate coord = new Coordinate();
		for(int row=0; row<=maxY; row++){
			coord.y = row;
			out.write("\n<tr>");
			for(int column=0;column<=maxX; column++){
				coord.x = column;
				cell = cells.get(coord);
				if (cell == null){
				    continue;
				}
				rowspan = cell.rowspan;
				colspan = cell.colspan;
				cssClass = cell.cssClass;
				valign = cell.valign;
			    out.write("\n<td ");
				if (rowspan != null){
				    out.write(" rowspan=\"" + rowspan + "\"");
				}
    			if (colspan != null){
                    out.write(" colspan=\"" + colspan + "\"");
                }
    			if (cssClass != null){
                    out.write(" class=\"" + cssClass + "\"");
                }
    			if (valign != null){
                    out.write(" valign=\"" + valign+ "\"");
                }
		        out.write(">");
				cell.component.write(out);
				out.write("\n</td>");
			}
			out.write("\n</tr>");
		}
		out.write("\n</table>");
	}

	
	
	@Override
	public void setPresentationMode(PresentationMode presentationMode) {
		super.setPresentationMode(presentationMode);
		for(Cell cell: cells.values()){
			cell.component.setPresentationMode(presentationMode);
		}
	}
	
	
	/**
	 * This method returns the number of rows that this table has.
	 * */
	public int getMaxRowIndex(){
		return maxY;
	}
	
	/**
	 * This method returns the number of columns that this table has.
	 * */
	public int getMaxColumnIndex(){
		return maxX;
	}
	
	
	/**
	 * This method returns the coordinate of the specified component, if it is in the table. Null otherwise.
	 * */
	public Coordinate getComponentCoordinate(BaseComponent component){
		Cell cell;
		for(Coordinate coord: cells.keySet()){
			cell = cells.get(coord);
			if(cell.component == component){
				return new Coordinate(coord.x,coord.y);
			}
		}
		return null;
	}
	
	
	/**
	 * Changes the presentation mode of the entire component line.
	 * */
	public void changePresentationModeOfEntireLine(BaseComponent component, PresentationMode pMode){
		Coordinate coord = getComponentCoordinate(component);
		Cell cell;
		for(int x=0;x<=maxX;x++){
			coord.x=x;
			cell = cells.get(coord);
			if(cell!=null){
				cell.component.setPresentationMode(pMode);
			}
		}
	}
	
	
	/**
	 * For debugging purposes, print the content of the table.
	 * */
	@SuppressWarnings("unused")
	private void debugContent(){
		logger.debug("Debugging table content.");
		Coordinate coord = new Coordinate();
		for(int y=0; y<=maxY;y++){
			for(int x=0; x<=maxX;x++){
				coord.x=x;
				coord.y=y;
				Cell comp = cells.get(coord);
				logger.debug("Component at ("+x+","+y+") is '"+(comp!=null?comp.component.getValue():"null")+"'");
			}
		}
	}


	/**
	 * Sets the rowspan for a given cell.
	 * */
    public void setRowspan(Integer rowspan, int column, int row) {
        Cell cell = cells.get(new Coordinate(column, row));
        cell.rowspan = rowspan;
    }
    
    /**
     * Sets the colspan for a given cell.
     * */
    public void setColspan(Integer colspan, int column, int row) {
        Cell cell = cells.get(new Coordinate(column, row));
        cell.colspan = colspan;
    }
    
    /**
     * Sets the cssClass for a given cell.
     * */
    public void setCssClass( String cssClass, int column, int row) {
        Cell cell = cells.get(new Coordinate(column, row));
        cell.cssClass = cssClass;
    }
    
    /**
     * Sets the vertical alignment for a given cell.
     * */
    public void setVerticalAlign(VerticalAlign valign, int column, int row) {
        Cell cell = cells.get(new Coordinate(column, row));
        cell.valign = valign;
    }
	
    
    /**
     * Sets the cssClass for a given cell.
     * */
    public void setCell(Cell cell, int column, int row) {
        cells.put(new Coordinate(column, row), cell);
    }


    /**
     * Removes all cells.
     * */
    @Override
    public void clearInterface() {
        cells.clear();
    }
}
